package com.starsoft.trade.service;

import static com.starsoft.frame.util.NumberUtil.addNullable;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.math3.stat.descriptive.moment.Variance;
import org.apache.commons.math3.stat.descriptive.summary.Sum;
import org.apache.commons.math3.stat.regression.SimpleRegression;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.starsoft.frame.util.DateUtil;
import com.starsoft.trade.bean.TradeSummary;
import com.starsoft.trade.bean.TradeSummaryId;
import com.starsoft.trade.dao.TradeSummaryDao;

@Service
public class TradeSummaryService {

	private static Logger logger = LoggerFactory.getLogger(TradeSummaryService.class);

	@Autowired
	TradeSummaryDao summaryDao;

	public List<TradeSummary> getAcountAll(Integer acountId) {
		return summaryDao.getAcountAll(acountId);
	}
	
	public TradeSummary get(Integer acountId, Date asofdate){
		TradeSummaryId id = new TradeSummaryId(acountId, asofdate);
		TradeSummary summary = summaryDao.get(id);
		return summary;
	}
	
	public void save(TradeSummary summary){
		summaryDao.saveOrUpdate(summary);
	}

	public Date getLastSummaryDate(Integer acountId, Date date){
		return summaryDao.getLastSummaryDate(acountId, date);
	}
	
	public List<TradeSummary> getAcountBetween(Integer acountId, Date beginDate, Date endDate){
		if(beginDate==null && endDate==null){
			return summaryDao.getAcountAll(acountId);
		} else if(beginDate==null){
			return summaryDao.getAcountBefore(acountId, endDate);
		} else if(endDate==null){
			return summaryDao.getAcountAfter(acountId, beginDate);
		} else {
			return summaryDao.getAcountBetween(acountId, beginDate, endDate);
		}
	}
	
	public void updateAnalysis(TradeSummary summary) {
		Date asofDate = summary.getId().getAsofdate();
		Date date1m  = DateUtil.bumpDate(asofDate, 0, -1, 0);
		Date date3m  = DateUtil.bumpDate(asofDate, 0, -3, 0);
		Date date6m  = DateUtil.bumpDate(asofDate, 0, -6, 0);
		Date date12m  = DateUtil.bumpDate(asofDate, 0, -12, 0);
		Integer acountId = summary.getId().getAcountId();
		List<TradeSummary> summaryList = summaryDao.getAcountBefore(acountId, asofDate);
		
		SimpleRegression regression = new SimpleRegression();
		Sum sum = new Sum();  
        Variance variance = new Variance();
		
		List<Double> baseProfitList = new ArrayList<Double>(summaryList.size());
		List<Double> profitList = new ArrayList<Double>(summaryList.size());
		double[] profitArray = null;
		double[] baseProfitArray = null;
		for (int i = summaryList.size() -1; i >= 0; i--) {
			TradeSummary summaryHis = summaryList.get(i);
			if(date1m.after(summaryHis.getId().getAsofdate())){
				profitArray = listToArray(profitList);
				baseProfitArray = listToArray(baseProfitList);
				summary.setProfit1m(sum.evaluate(profitArray));
				summary.setBaseProfit1m(sum.evaluate(baseProfitArray));
				summary.setVolability1m(variance.evaluate(profitArray));
			}else if(date3m.after(summaryHis.getId().getAsofdate())){
				profitArray = listToArray(profitList);
				baseProfitArray = listToArray(baseProfitList);
				summary.setProfit3m(sum.evaluate(profitArray));
				summary.setBaseProfit3m(sum.evaluate(baseProfitArray));
				summary.setVolability3m(variance.evaluate(profitArray));
			} else if(date6m.after(summaryHis.getId().getAsofdate())){
				profitArray = listToArray(profitList);
				baseProfitArray = listToArray(baseProfitList);
				summary.setProfit6m(sum.evaluate(profitArray));
				summary.setBaseProfit6m(sum.evaluate(baseProfitArray));
				summary.setVolability6m(variance.evaluate(profitArray));
			} else if(date12m.after(summaryHis.getId().getAsofdate())){
				profitArray = listToArray(profitList);
				baseProfitArray = listToArray(baseProfitList);
				summary.setProfit12m(sum.evaluate(profitArray));
				summary.setBaseProfit12m(sum.evaluate(baseProfitArray));
				summary.setVolability12m(variance.evaluate(profitArray));
			}
			baseProfitList.add(summaryHis.getBaseProfitDay());
			profitList.add(summaryHis.getProfitDay());
			regression.addData(summaryHis.getBaseProfitDay(), summaryHis.getProfitDay());
		}
		profitArray = listToArray(profitList);
		summary.setAlpha(regression.getIntercept());
		summary.setBeta(regression.getSlope());
		summary.setVolability(variance.evaluate(profitArray));
	}

	public TradeSummary caculateSummary(Date asofdate, TradeSummary lastSummary, double cashChg, double principleChg,
			double marketValue, double baseValue) {
		TradeSummaryId id = new TradeSummaryId(lastSummary.getId().getAcountId(), asofdate);
		TradeSummary summary = new TradeSummary(id);
		summary.setPrincipal(lastSummary.getPrincipal() + principleChg);
		summary.setCash(lastSummary.getCash() + cashChg);
		summary.setMarketValue(marketValue);
		summary.setNetAssert(summary.getCash() + marketValue);
		summary.setBaseValue(baseValue);

		Double profitDayMoney = summary.getNetAssert() - principleChg - lastSummary.getNetAssert();
		summary.setProfitDayMoney(profitDayMoney);
		summary.setProfitMoney(profitDayMoney + lastSummary.getProfitMoney());

		Double dayPer = profitDayMoney / (lastSummary.getNetAssert() + principleChg);
		Double per = (dayPer + 1) * (lastSummary.getProfit() + 1) - 1;
		summary.setProfitDay(dayPer);
		summary.setProfit(per);

		Double baseDayPer = summary.getBaseValue() / lastSummary.getBaseValue() - 1;
		Double basePer = (baseDayPer + 1) * (lastSummary.getBaseProfit() + 1) - 1;
		summary.setBaseProfitDay(baseDayPer);
		summary.setBaseProfit(basePer);

		return summary;
	}

	private double[] listToArray(List<Double> datas) {
		double[] result = new double[datas.size()];
		for (int i = 0; i < datas.size(); i++) {
			result[i] = datas.get(i);
		}
		return result;
	}
	
	public TradeSummary combine(TradeSummary summaryA, TradeSummary summaryB) {	
		double snetAssertA = summaryA.getNetAssert() / (1 + summaryA.getProfit());
		double snetAssertB = summaryB.getNetAssert() / (1 + summaryB.getProfit());
		double profitMoney = summaryA.getProfit() * snetAssertA + summaryB.getProfit() * snetAssertB;
		double profit = profitMoney / (snetAssertA + snetAssertB);
		summaryA.setProfit(profit);
		summaryA.setProfitMoney(addNullable(summaryA.getProfitMoney(), summaryB.getProfitMoney()));

		double profitDayMoney = addNullable(summaryA.getProfitDayMoney(), summaryB.getProfitDayMoney());
		double profitDay = profitDayMoney / (summaryA.getNetAssert() + summaryB.getNetAssert() - profitDayMoney);
		summaryA.setProfitDay(profitDay);
		summaryA.setProfitDayMoney(profitDayMoney);

		summaryA.setPrincipal(addNullable(summaryA.getPrincipal(), summaryB.getPrincipal()));
		summaryA.setNetAssert(addNullable(summaryA.getNetAssert(), summaryB.getNetAssert()));
		summaryA.setMarketValue(addNullable(summaryA.getMarketValue(), summaryB.getMarketValue()));
		summaryA.setCash(addNullable(summaryA.getCash(), summaryB.getCash()));
		
		return summaryA;
	}
}
